﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

using alib.Debugging;
using alib.Enumerable;
using alib.String;
using alib.String.Builder;
using alib.Concurrency;

namespace agree
{
#if false
	public abstract partial class ChartControl<T, W> : ChartResources<W>
		where T : ChartControl<T, W>
		where W : struct, IProximalContext<W>
	{
		public abstract partial class _chart : _has_grammar, IEnumerable<IChartObj<W>>
		{
			public partial struct NotificationSource : IEnumerable<ActiveEdge>
			{
				public String _dbg_display_icol(int i_col)
				{
					if (m_chart is ParseControl.Chart)
					{
						bool f_left = (i_col & 1) == 0;
						int j = i_col >> 1;
						if (f_left)
							j++;
						return String.Format("Parse objects which are to the {0} of column {1}", f_left ? "left" : "right", j);
					}
					else
					{
						return String.Format("Generation objects containing {0} EPs", i_col);
					}
				}
			}
		};
	};
#endif


#if DEBUG
#if false
	public partial class prs_atfs_nary_pack  : atfs_nary_pack<ParseControl, ??>// : ArrayTfs, IChartObj, IPacking
	{
		public String TreeDisplay()
		{
			Debug.Assert(Daughters.Length > 0);
			int id = 0;
			//var uptfs = UnpackedTfs == null ? "**NULL**" : UnpackedTfs.id.ToString("X");
			String cc;
			if (this is PassiveEdge)
			{
				BitArr ba = ccc.Bits;
				if (ba == null)
					cc = String.Format("{0} {1}", new String('0', ((GenControl)(Object)ctrl).input_mrs.rels.Length), base.ToString());
				else
					cc = String.Format("{0} {1}", ccc.Bits, base.ToString());
			}
			else
				cc = this._debug_display;

			String s = String.Format("{0} {{{1}}} {{{2}}}{3} {4} {{{5}}}",// {6:X}",
				cc,
				id.ToString("X"),
				this.Tfs.id.ToString("X"),
				this.Tfs.AnyRestriction ? " R" : "",
				this.Tfs.Type.Name,
				this.License.Name
				//uptfs
				/*, DerivationHash*/) + Environment.NewLine;
			foreach (var pt in Daughters)
			{
				s += pt.TreeDisplay().Indent(4) + Environment.NewLine;
			}
			return s.TrimEnd();
		}

		public void DumpDerivations(TextWriter tw = null)
		{
			tw = tw ?? Console.Out;
			System.Text.StringBuilder sb = null;
			if (_packed_edges == null)
				return;

			sb = new System.Text.StringBuilder();
			sb.AppendFormatLine("================== {0} ================", this.ToString());

			sb.AppendFormatLine("daughters:");
			IDerivation[][] rgrg = new IDerivation[rgo.Length][];
			for (int j = 0; j < rgo.Length; j++)
			{
				rgrg[j] = rgo[j].Derivations.ToArray();

				sb.AppendFormatLine("    #{0} derivations: {1}", j, rgrg[j].Length);
				for (int k = 0; k < rgrg[j].Length; k++)
				{
					sb.AppendFormatLine("        {0}", rgrg[j][k].ToString());
				}
			}

			sb.AppendLine();

			sb.AppendFormatLine("packed: {0} edges", _packed_edges.Count);
			int i = 0;
			foreach (PassiveEdge2<T> dpe in _packed_edges)
			{
				sb.AppendFormatLine("    packed edge #{0} {1} derivations: {2}", i++, dpe.ToString(), dpe.Derivations.Count);
				foreach (IDerivation dv in dpe.Derivations)
				{
					Object o = dv is ChartDerivation<T> ? (Object)((ChartDerivation<T>)dv).ParseObj : dv;
					sb.AppendFormatLine("    === dv #{0} {1} ===", i++, o.ToString());
					//sb.AppendFormatLine("{0}", dv.TreeDisplay().ToString());
					if (dv is ChartDerivation<T>)
						sb.AppendLine(((ChartDerivation<T>)dv).Daughters.Select(d => d.ToString()).StringJoin(Environment.NewLine).Indent(8));
					//else
					//    sb.AppendLine("        " + ((LexicalAnalysis.AnalysisStack)dv).ToString());
					sb.AppendLine();

					//sb.AppendFormatLine("        {0}", dpe.rgo[j].ToString());
					//for (int j = 0; j < rgo.Count; j++)
					//{
					//    sb.AppendFormatLine("        {0}", dpe.rgo[j].ToString());
					//}
				}
			}
			sb.AppendLine();

			sb.AppendFormatLine("cross-product: {0}", _parse_trees.Length);
			i = 0;
			foreach (IDerivation dv in _parse_trees)
			{
				Object o = dv is ChartDerivation<T> ? (Object)((ChartDerivation<T>)dv).ParseObj : dv;
				sb.AppendFormatLine("    === dv #{0} {1} ===", i++, o.ToString());
				//sb.AppendFormatLine("{0}", dv.TreeDisplay().ToString());
				if (dv is ChartDerivation<T>)
					sb.AppendLine(((ChartDerivation<T>)dv).Daughters.Select(d => d.ToString()).StringJoin(Environment.NewLine).Indent(8));
				//else
				//    sb.AppendLine("        " + ((LexicalAnalysis.AnalysisStack)dv).ToString());
				sb.AppendLine();
			}
			sb.AppendLine();

			tw.WriteLine(sb.ToString());
		}
		//public IList<String> DerivationToSurfaceForm(ChartControl<T,W>.IChartObj db)
		//{
		//    if (db.License is LexicalEntry)
		//    {
		//        IList<String> tok_list = new List<String>();
		//        tok_list.Add(((LexicalEntry)db.License).Lemmata.StringJoin(" "));
		//        return tok_list;
		//    }
		//    else if (db.License is LexicalRule)
		//    {
		//        if (((PassiveEdge<T>)db).Daughters.Length != 1)
		//        {
		//            throw new Exception("Morphological rule had 0 or more than 1 daughter");
		//        }

		//        IList<String> daughter_strs = DerivationToSurfaceForm((((PassiveEdge<T>)db).Daughters)[0]);
		//        string daughter = daughter_strs[0];


		//        List<Lexicon.RuleForm> irregs;
		//        if (tm.g.lex.reverse_irreg_dict.TryGetValue(daughter_strs[0], out irregs))
		//        {
		//            foreach (var ir in irregs)
		//            {
		//                if (ir.rule.Name == db.License.Name)
		//                {
		//                    daughter_strs[0] = ir.form; //not actually the stem but the inflected form (just reusing the same field as for parsing)
		//                    return daughter_strs;
		//                }
		//            }
		//        }

		//        if (db.License is MorphologicalRule)
		//        {
		//            MorphologicalRule mr = db.License as MorphologicalRule;

		//            foreach (var sr in mr.Subrules)
		//            {
		//                if (sr.rev_regex.IsMatch(daughter_strs[0]))
		//                {
		//                    daughter_strs[0] = sr.rev_regex.Replace(daughter_strs[0], sr.rev_replace);
		//                    return daughter_strs;
		//                }
		//            }
		//        }

		//        return daughter_strs;
		//    }
		//    else if (db.License is SyntaxRule)
		//    {
		//        List<String> output_toks = new List<String>();
		//        foreach (var daughter in (((PassiveEdge<T>)db).Daughters))
		//        {
		//            output_toks.AddRange(DerivationToSurfaceForm(daughter));
		//        }

		//        return output_toks;
		//    }
		//    throw new Exception();
		//}
	};
#endif
#endif

	public partial class ParseControl : ChartControl<ParseControl, ChartSpan>
	{
#if false
			[Conditional("DEBUG")]
			public void CheckDuplicateDerivations()
			{
				ConcurrentHashSet<ulong> d_hashes = new ConcurrentHashSet<ulong>();

				int c_derivations = 0;
				foreach (var drv in this.ValidatedRootDerivations)
				{
					d_hashes.Add(drv.DerivationHash);
					c_derivations++;
				}

				if (c_derivations != d_hashes.Count)
				{
					//throw new Exception(String.Format("{0} duplicate derivations", c_derivations - d_hashes.Count));
					var grps = this.ValidatedRootDerivations.GroupBy(drv => drv.DerivationHash).Where(grp => grp.MoreThanOne());
					Console.WriteLine(String.Format("{0} duplicate derivations in {1} groups", c_derivations - d_hashes.Count, grps.Count()));

					List<String> l0 = new List<String>(), l1 = new List<String>();
					foreach (var grp in grps)
					{
						l0.Add(grp.ElementAt(0).TreeDisplay());
						l1.Add(grp.ElementAt(1).TreeDisplay());
						System.IO.File.WriteAllLines(String.Format("000.tfs-dump"), l0);
						System.IO.File.WriteAllLines(String.Format("001.tfs-dump"), l1);

						Console.WriteLine("=========================");
						foreach (var drv in grp)
						{
							Console.WriteLine("{0}", drv.TreeDisplay());
							Console.WriteLine();
						}
					}
				}
				//if (rgdrv != null)
				//{
				//    if (rgdrv.Count != hsdrv.Count)
				//        report.AppendLine(String.Format("derivations: {0} --- distinct: {1}    (extra {2})", rgdrv.Count, hsdrv.Count, rgdrv.Count - hsdrv.Count));
				//}

			}
#endif

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// DEBUG CODE FOLLOWS
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[Conditional("DEBUG")]
		public void ChartEdgeReport(IChartObj<ChartSpan> ce)
		{
			String span_color = IsSpanning(ce) ? "$yellow" : "$darkyellow";
			String sis = "";// .ToString(ce);
			TextWriter debug_output = SysObj.Instance.tw_debug;
			if (ce == null)
			{
				debug_output.WriteLineColor("$darkcyan {0,-18} $green E{2,-4} " + span_color + " {3,-7} $green {4}",
					ce.GetType().Name,
					null,
					sis,
					ce.ToString(),
					ce.Tfs);
			}
			else
			{
				String fedge = ce.Tfs.ToString();
				String medge = fedge.Replace("@", "$green @").Replace("…", "…$darkgreen ");
				debug_output.WriteLineColor(new String(' ', 25) + span_color + " {0,-7}       $darkgreen {1}$ added by $darkgreen {2}$ is now $darkcyan {3}$ $green E{4,-4}",
					ce.ToString(),
					medge.PadRight(35 + (medge.Length - fedge.Length)),
					ce.Daughters.Select(de => String.Format("{0}", de.ToString())).StringJoin("$ + $darkgreen ").PadLeft(9 + (ce.Daughters.Count - 1) * 12),
					ce.GetType().Name,
					sis);
			}
		}

#if false
		[Conditional("DEBUG")]
		public void DumpChart(String html_out_file)
		{

			var derivations = ParentlessEdges
				.Select((IDerivation de, int ix) => new { top = de, ix, dx = de.DescendantsAndSelf() });

			var edge_lookup = derivations
								.SelectMany(dv => dv.top.DescendantsAndSelf().Select(e => new { top = dv, dv.ix, e }))
								.ToLookup(de => de.e, de => new { de.ix, de.top });

			StringBuilder sb1 = new StringBuilder();
			foreach (var derivation in derivations)
			{
				sb1.AppendFormat(@"
.dv{0}
{{
}}", derivation.ix);
			}

			StringBuilder sb = new StringBuilder();
			sb.AppendFormat(@"
<html>
<head>
<style>
body
{{
font-family:Arial;
}}
span
{{
cursor: pointer;
}}
.fill
{{
background-color:#f0f0f0;
}}
table tr td
{{
vertical-align:top;
font-size:smaller;
}}
{0}
</style>
</head>
<body>
", sb1.ToString());
			int c = this.chart.Length;
			double grid_cells_x = 120;// Enumerable.Range(1, c - 1).LeastCommonMultiple();

			sb.Append("<table border=1><tr>");
			int cx_total = 0;
			for (int j = 0; j < c; j++)
			{
				int cx = (int)Math.Round(grid_cells_x * (j + 1) / c) - cx_total;
				cx_total += cx;
				sb.AppendFormat("<th colspan={0}>{1}</th>\r\n", cx, j);
			}
			sb.Append("</tr>\r\n\r\n");

			var dict = AllEdges().Cast<IChartObj<ChartSpan>>()._GroupBy(e => e.ProximalContext.CoverageCount)
							.ToDictionary(g => g.Key, g => g.ToLookup(e =>
								{
									return e.ProximalContext.StartIndex;
								}));

			for (int span = c; span > 0; span--)
			{
				sb.Append("<tr>\r\n");

				ILookup<int, IChartObj<ChartSpan>> length_starts_map;
				if (dict.TryGetValue(span, out length_starts_map))
				{
					cx_total = 0;
					int items = c - span + 1;
					for (int k = 0; k < items; k++)
					{
						int cx = (int)Math.Round(grid_cells_x * (k + 1) / items) - cx_total;
						cx_total += cx;

						IEnumerable<IChartObj> edges = length_starts_map[k];

						String s_cont;
						if (edges != null && edges.Any())
						{
							StringBuilder content = new StringBuilder();
							foreach (var pe in edges)
							{
								String kk = edge_lookup[pe].Select(a => String.Format("dv{0}", a.ix)).StringJoin(" ");
								String b = pe is IRootDerivations ? "style='font-weight:bold;' " : String.Empty;
								content.AppendFormat("\t<span {0}class='{1}'>{2}</span><br />\r\n", b, kk, pe.ToString().Replace("-", "&#8209;"));
							}
							s_cont = content.ToString();
						}
						else
							s_cont = "&nbsp;";

						sb.AppendFormat("\t<td colspan='{0}'>\r\n{1}</td>\r\n", cx, s_cont);
					}
				}
				sb.Append("</tr>\r\n");
			}

			sb.AppendFormat("</table>");
			sb.Append(@"
<script type='text/javascript'>
	function getCSSRule(ruleName) {
		ruleName = ruleName.toLowerCase();
		if (document.styleSheets) {
			for (var i = 0; i < document.styleSheets.length; i++) {
				var styleSheet = document.styleSheets[i];
				var ii = 0;
				var cssRule = null;
				do {
					if (styleSheet.cssRules) {
						cssRule = styleSheet.cssRules[ii];
					} else {
						cssRule = styleSheet.rules[ii];
					}
					if (cssRule) {
						if (cssRule.selectorText.toLowerCase() == ruleName) {
							return cssRule;
						}
					}
					ii++;
				} while (cssRule)
			}
		}
		return null;
	}

	function ms_in(e) {
		var x = e.className.split(' ');
		for (var z = 0; z < x.length; z++) {
			var r = getCSSRule('.' + x[z]);
			if (r != null)
				r.style.color = 'red';
		}
	}

	function ms_out(e) {
		var x = e.className.split(' ');
		for (var z = 0; z < x.length; z++) {
			var r = getCSSRule('.' + x[z]);
			if (r != null)
				r.style.color = 'black';
		}
	}

	var obj = document.getElementsByTagName('span'), o, i = 0;
	while (o = obj[i++]) {
		o.onmouseover = function() { ms_in(this); };
		o.onmouseout = function() { ms_out(this); };
	}
</script>
");
			sb.AppendFormat("</body></html>");
			File.WriteAllText(html_out_file, sb.ToString());
		}
#endif
	};
}
